package com.xf.system.utils.redis;

import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.jsontype.impl.LaissezFaireSubTypeValidator;
import org.springframework.cache.annotation.CachingConfigurerSupport;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.cache.RedisCacheConfiguration;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializationContext;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;

import java.time.Duration;

/**
 * @author xiefengf
 *
 * Redis配置类
 */
@Configuration
@EnableCaching
public class RedisConfig extends CachingConfigurerSupport {

    /**
     *   RedisTemplate 对存储的 key 和 value 配置序列化
     */
    @Bean
    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {
        // 创建一个新的RedisTemplate对象，模板默认使用JDK序列化，但这里我们会修改它
        RedisTemplate<String, Object> template = new RedisTemplate<>();
        // 创建一个String类型的序列化器，用于序列化Redis的key
        RedisSerializer<String> redisSerializer = new StringRedisSerializer();
        // Jackson 提供一些功能将数据集或对象转换的实现（解决查询缓存转换异常的问题）
        Jackson2JsonRedisSerializer<Object> jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer<>(Object.class);
        ObjectMapper om = new ObjectMapper();
        om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        om.activateDefaultTyping(LaissezFaireSubTypeValidator.instance,ObjectMapper.DefaultTyping.NON_FINAL,JsonTypeInfo.As.PROPERTY);
        jackson2JsonRedisSerializer.setObjectMapper(om);
        // 指定序列化器
        template.setConnectionFactory(factory);
        //指定键（key）的序列化器
        template.setKeySerializer(redisSerializer);
        //指定值（value）的序列化器
        template.setValueSerializer(jackson2JsonRedisSerializer);
        // Hash key的序列化
        template.setHashKeySerializer(redisSerializer);
        // Hash value的序列化
        template.setHashValueSerializer(jackson2JsonRedisSerializer);
        // 调用RedisTemplate的afterPropertiesSet方法，该方法用于完成一些初始化的工作 (在Spring的Bean生命周期中，这通常在Bean属性设置之后被调用)
        template.afterPropertiesSet();
        return template;
    }

    /**
     * 缓存管理器
     * 默认的RedisTemplate和RedisCacheManager的使用JDK的序列化方式，可以自定义为string和json序列化
     */
    @Bean
    public RedisCacheManager myRedisCacheManager(RedisConnectionFactory factory) {
        // 创建一个Jackson2JsonRedisSerializer实例，用于将Java对象序列化为JSON字符串，并反序列化回Java对象 - Jackson 提供一些功能将数据集或对象转换的实现（解决查询缓存转换异常的问题）
        Jackson2JsonRedisSerializer<Object> jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer<>(Object.class);
        // 创建一个ObjectMapper实例，它是Jackson库的核心类，用于处理JSON序列化与反序列化
        ObjectMapper om = new ObjectMapper();
        // 设置ObjectMapper的可见性，使其能够访问所有属性的getter方法，无论其可见性如何
        om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        // 启用默认类型信息，允许在JSON中包含Java对象的类信息，以便在反序列化时恢复为正确的类型 - 注意：这可能会带来安全风险，因为它允许潜在的攻击者通过恶意的JSON数据触发不期望的代码执行，因此，在生产环境中使用时需要谨慎
        om.activateDefaultTyping(LaissezFaireSubTypeValidator.instance, ObjectMapper.DefaultTyping.NON_FINAL, JsonTypeInfo.As.PROPERTY);
        // 将配置好的ObjectMapper设置到Jackson2JsonRedisSerializer中
        jackson2JsonRedisSerializer.setObjectMapper(om);
        // 配置Redis缓存的序列化方式和过期时间
        // 使用RedisCacheConfiguration.defaultCacheConfig()作为默认配置
        // 设置缓存项的过期时间为600秒
        // 使用StringRedisSerializer序列化key
        // 使用之前创建的jackson2JsonRedisSerializer序列化value
        // 禁用缓存空值（null values）
        RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig()
                .entryTtl(Duration.ofSeconds(600))
                .serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(new StringRedisSerializer()))
                .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(jackson2JsonRedisSerializer))
                .disableCachingNullValues();
        // 使用提供的RedisConnectionFactory和缓存配置创建一个RedisCacheManager实例
        // 设置默认缓存配置为之前创建的config
        // 构建并返回RedisCacheManager实例
        return RedisCacheManager.builder(factory)
                .cacheDefaults(config)
                .build();
    }

}
